import java.util.Vector;

public class Proc {

	private String name;
	private Proc parent;
	private int static_depth = 0;
	public Vector<Proc> subproc = new Vector<Proc>();
	public Vector<String> local_var = new Vector<String>();
	public Vector<String> formal_par = new Vector<String>();
	public Vector statement = new Vector();

	public Proc(String name, Proc parent) {
		this.name = name;
		this.parent = parent;
		if (parent != null)
			static_depth = parent.static_depth + 1;
	}

	public void execute() {
		//
		// execute the statements of this procedure one by one
		// using exec_assignment(...) or exec_proc_call(...) depending
		// on the type of the respective statement.
		//
		for (int i = 0; i < statement.size(); i++) {
			if (statement.elementAt(i) instanceof Assignment)
				execAssignment((Assignment) statement.elementAt(i));
			else
				execProcCall((ProcCall) statement.elementAt(i));
		}
	}

	private void execAssignment(Assignment a) {
		Object value = a.right_hand_side.eval();
		Stack.getCellOf(a.left_hand_side)[0] = value;
	}

	private void execProcCall(ProcCall c) {
		Proc proc = null;

		if ((c.condition_x == null && c.condition_y == null)
				|| !Stack.getCellContentsOf(c.condition_x).equals(
						Stack.getCellContentsOf(c.condition_y))) {
			proc = findProc(c.name, this);

			if (proc == null) {
				throw new RuntimeException("DÅLIGT!!!");
			}

			proc.initRecord(c.actual_par, this.static_depth);
			proc.execute();
			Stack.deleteRecord();
		}
	}

	private Proc findProc(String name, Proc proc) {
		for (Proc p : proc.subproc) {
			if (name.equals(p.name)) {
				return p;
			}
		}

		if (proc.parent == null) {
			throw new RuntimeException(
					"Funktionen finns ej!!! Du är dålig!!!!!");
		}

		return findProc(name, proc.parent);
	}

	protected void initRecord(Vector<String> actual, int calling_depth) {
		Stack.addRecord(calling_depth - this.parent.static_depth);

		for (int i = 0; i < actual.size(); i++) {
			String name = actual.elementAt(i);
			String formalName = formal_par.elementAt(i);
			if (Stack.getCellOf(name) == null) {
				throw new RuntimeException("JÄTTEDÅLIGT");
			}

			Stack.addVariable(formalName, Stack.getCellOf(name));
		}

		for (String name : this.local_var) {
			Stack.addVariable(name, null);
		}
	}
}
